﻿using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;

namespace Cap.Testing
{
    public class RabbtEventSource : IEventSource
    {
        public ConnectionFactory ConnectionFactory { get; set; } = new ConnectionFactory();

        public IEventSourceProvider Build(string subject)
        {
            return new RabbitEventProvider(subject, this);
        }

        class RabbitEventProvider : IEventSourceProvider
        {
            private IModel? _model;

            private IConnection? _connection;

            private readonly string _subject;

            private readonly RabbtEventSource _source;

            public event EventHandler<ICloudEvent>? Received;

            public RabbitEventProvider(string subject, RabbtEventSource source)
            {
                _subject = subject;
                _source = source;
            }

            public Task StartAsync(CancellationToken cancellationToken)
            {
                _connection = _source.ConnectionFactory.CreateConnection();
                _model = _connection.CreateModel();
                _model.ExchangeDeclare(exchange: "logs", type: ExchangeType.Fanout);

                // declare a server-named queue
                var queueName = _subject;
                _model.QueueDeclare(_subject, true, false, false);
                _model.QueueBind(queue: queueName,
                                 exchange: "logs",
                                 routingKey: string.Empty);
                var consumer = new EventingBasicConsumer(_model);
                consumer.Received += ConsumerReceived;
                _model.BasicConsume(queue: queueName,
                                    autoAck: true,
                                    consumer: consumer);
                return Task.CompletedTask;
            }

            public Task StopAsync(CancellationToken cancellationToken)
            {
                _model?.Close();
                _connection?.Close();
                return Task.CompletedTask;
            }

            private void ConsumerReceived(object? sender, BasicDeliverEventArgs args)
            {
                byte[] body = args.Body.ToArray();
                Received?.Invoke(this, new CloudEvent(_subject, body));
            }
        }
    }
}
